// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DepthCameraIntrinsicParameters.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Microsoft.Robotics.Vision.Cameras
{
    using System;
    using System.Runtime.Serialization;
    using Microsoft.Robotics.Numerics;

    /// <summary>
    /// Depth camera intrinsic parameters. Note that some of these are not really constants, and can be overridden by deserializing a config file. 
    /// </summary>
    [DataContract]
    public class DepthCameraIntrinsicParameters
    {
        /// <summary>
        /// The theoretical focal length of depth image in pixels, adjusted for resolution of 640x480.
        /// From Kinect for Windows.
        /// </summary>
        public const double KinectNominalFocalLengthInPixels = 2 * 285.63;
        
        /// <summary>
        /// The only Kinect horizontal resolution we support. 
        /// </summary>
        public const int KinectHorizontalResolution = 640;

        /// <summary>
        /// The only Kinect vertical resolution we support. 
        /// </summary>
        public const int KinectVerticalResolution = 480;

        /// <summary>
        /// The number of dead pixel columns on the side of the image
        /// </summary>
        public const int KinectDeadPixelCount = 8;

        /// <summary>
        /// The time it takes to complete a rolling shutter read of the Kinect IR sensor.
        /// </summary>
        public const double KinectRollingShutterReadoutTime = 0.03055;

        /// <summary>
        /// The horizontal resolution, in pixels. 
        /// </summary>
        [DataMember]
        public readonly int HorizontalResolution;

        /// <summary>
        /// The vertical resolution, in pixels. 
        /// </summary>
        [DataMember]
        public readonly int VerticalResolution;

        /// <summary>
        /// The horizontal center of the image plane. 
        /// </summary>
        [DataMember]
        public readonly double HorizontalCenter;

        /// <summary>
        /// The vertical center of the image plane. 
        /// </summary>
        [DataMember]
        public readonly double VerticalCenter;

        /// <summary>
        /// The number of dead pixels at the left side of the depth image
        /// </summary>
        [DataMember]
        public readonly int DeadPixelCount;

        /// <summary>
        /// The time it takes to complete a rolling shutter read of the Kinect IR sensor.
        /// </summary>
        [DataMember]
        public readonly double RollingShutterReadoutTime;

        /// <summary>
        /// The measured focal length of depth image in pixels, given a resolution of 640x480.
        /// </summary>
        [DataMember]
        public readonly double HorizontalFocalLengthInPixels; 

        /// <summary>
        /// Gets the inverse of the HorizontalFocalLengthInPixels, or 1/HorizontalFocalLengthInPixels.
        /// </summary>
        [DataMember]
        public readonly double InverseHorizontalFocalLengthInPixels;

        /// <summary>
        /// The measured focal length of depth image in pixels, given a resolution of 640x480.
        /// </summary>
        [DataMember]
        public readonly double VerticalFocalLengthInPixels;

        /// <summary>
        /// Gets the inverse of the VerticalFocalLengthInPixels, or 1/VerticalFocalLengthInPixels.
        /// </summary>
        [DataMember]
        public readonly double InverseVerticaFocalLengthInPixels;

        /// <summary>
        /// Horizontal field of view of the Kinect camera.
        /// </summary>
        [DataMember]
        public readonly double HorizontalFieldOfViewInRadians;

        /// <summary>
        /// Vertical field of view of the Kinect camera.
        /// </summary>
        [DataMember]
        public readonly double VerticalFieldOfViewInRadians;

        /// <summary>
        /// Initializes a new instance of the DepthCameraIntrinsicParameters class.
        /// </summary>
        public DepthCameraIntrinsicParameters()
            : this(KinectNominalFocalLengthInPixels, KinectNominalFocalLengthInPixels, KinectHorizontalResolution / 2, KinectVerticalResolution / 2)
        {
        }

        /// <summary>
        /// Initializes a new instance of the DepthCameraIntrinsicParameters class.
        /// </summary>
        /// <param name="horizontalFocalLengthInPixels">The measured horizontal focal length</param>
        /// <param name="verticalFocalLengthInPixels">The measured vertical focal length</param>
        /// <param name="horizontalCenter">The measured horizontal center of the image</param>
        /// <param name="verticalCenter">The measured vertical center of the image</param>
        /// <param name="horizontalResolution">The horizontal resolution</param>
        /// <param name="verticalResolution">The vertical resolution</param>
        /// <param name="deadPixelCount">The number of dead pixels at the left side of the image</param>
        /// <param name="rollingShutterReadoutTime">The time it takes to complete a rolling shutter reading of the sensor</param>
        public DepthCameraIntrinsicParameters(
            double horizontalFocalLengthInPixels, 
            double verticalFocalLengthInPixels, 
            double horizontalCenter, 
            double verticalCenter, 
            int horizontalResolution = KinectHorizontalResolution, 
            int verticalResolution = KinectVerticalResolution,
            int deadPixelCount = KinectDeadPixelCount,
            double rollingShutterReadoutTime = KinectRollingShutterReadoutTime)
        {
            this.HorizontalResolution = horizontalResolution;
            this.VerticalResolution = verticalResolution;
            this.HorizontalFocalLengthInPixels = horizontalFocalLengthInPixels;
            this.VerticalFocalLengthInPixels = verticalFocalLengthInPixels;
            this.HorizontalCenter = horizontalCenter;
            this.VerticalCenter = verticalCenter;
            this.HorizontalFieldOfViewInRadians = 2 * Math.Atan2(horizontalResolution, 2 * horizontalFocalLengthInPixels);
            this.VerticalFieldOfViewInRadians = 2 * Math.Atan2(verticalResolution, 2 * verticalFocalLengthInPixels);
            this.InverseHorizontalFocalLengthInPixels = 1 / horizontalFocalLengthInPixels;
            this.InverseVerticaFocalLengthInPixels = 1 / verticalFocalLengthInPixels;
            this.DeadPixelCount = deadPixelCount;
            this.RollingShutterReadoutTime = rollingShutterReadoutTime;
        }
    }
}
